package CCNQ::Proxy::Bill;
# Copyright (C) 2006, 2007  Stephane Alnet
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 3
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
# 

#
# For more information visit http://carrierclass.net/
#
use strict; use warnings;

use base qw(CCNQ::Billing::Bill);
use CCNQ::Proxy::Base;

sub parse_raw
{
    my $self = shift;
    my ($t) = @_;
    return split(/\|/,$t);
}

sub _field_names
{
    # Default fields for OpenSER 1.2
    # Note: The names are the default from modules/acc/mod_acc.c
    #       The order is given by modules/acc/acc.c
    #       (Look for acc_method_col, etc.)
    my @field_names_12 = qw(
        method
        from_tag
        to_tag
        callid
        sip_code
        sip_reason
        time
    );

    my @cdr_extra = ();

    my @cdr_src = @{CCNQ::Proxy::Base::cdr_extra()};
    while(@cdr_src)
    {
        my $name = shift @cdr_src;
        my $var  = shift @cdr_src;
        push @cdr_extra, $name;
    }
    undef @cdr_src;

    return (@field_names_12,@cdr_extra);
}


sub process_record
{
    our $self = shift;
    our %fields = @_;

    sub _alert
    {
        my $what = shift;
        $self->warn("<b>Alert!</b>, $what: "
            .join( ';', map { qq($_=').(defined $fields{$_} ? $fields{$_} : '').q(') } sort keys %fields )
            ."<br>\n");        
    }

    # Do we need to protect against duplicate Call-IDs for different calls?
    # e.g. if a customer places multiple calls to different destinations with the same
    # Call-ID, how do we detect this?
    # How do we do it?
    my $callref = $fields{callid};

    # Check: should we ignore this call?
    # return 0 if $self->ignore_callref($callref);

    # Retrieve the existing partial record, if any.
    my $record = $self->retrieve_partial_record($callref);

    $record->{callref} = $callref;

    my $time   = $fields{time};
    my $method = $fields{method};

    if($method eq 'INVITE')
    {
        if($fields{sip_code} ne '200')
        {
            return 0;
        }

        $record->{start}        = $time
            unless exists $record->{start} and $time > $record->{start};

        # Originating and Terminating numbers
        $record->{src}          = $fields{from_user};
        $record->{dst}          = $fields{ruri_user};

        # Clean-up the numbers
        $record->{src} =~ s/^\+//;
        $record->{dst} =~ s/^\+//;
        # NANPA
        $record->{src} =~ s/^(\d{10})$/1$1/;
        $record->{dst} =~ s/^(\d{10})$/1$1/;
        # /NANPA

        if(defined($fields{src_type}))
        {
            if( defined($record->{src_type}) && $record->{src_type} ne $fields{src_type} )
            {
                _alert("src_type new=$fields{src_type} old=$record->{src_type}");
            }
            else
            {
                $record->{src_type}     = $fields{src_type};
            }
        }

        if(defined($fields{dst_type}))
        {
            if( defined($record->{dst_type}) && ($record->{dst_type} ne $fields{dst_type}))
            {
                _alert("dst_type new=$fields{dst_type} old=$record->{dst_type}");
            }
            else
            {
                $record->{dst_type}     = $fields{dst_type};
            }
        }

        our $subscriber;

        if( defined($record->{src_type}) && $record->{src_type} eq 'ONNET' )
        {
            $subscriber = $fields{src_subs}
                if defined $fields{src_subs} and $fields{src_subs} ne '';
        }

        if( defined($record->{dst_type}) && $record->{dst_type} eq 'ONNET' )
        {
            $subscriber = $fields{dst_subs}
                if defined $fields{dst_subs} and $fields{dst_subs} ne '';
        }

        if(not defined $subscriber)
        {
            _alert("no subscriber/trunk information");
            return 0;
        }

        if(not defined $record->{billable})
        {
            $record->{billable} = $self->{billing_info}->billto($subscriber);
        }
        
        if(not defined $record->{billable})
        {
            _alert("no account (subscriber $subscriber)");
        }

        $record->{src} = $self->{billing_info}->default_src($subscriber)
            if $record->{src} !~ /^\d+$/;

        $record->{billable_call} ||= 0;

        $record->{billable_call} = 1;
        _alert('Mismatched')
            if $self->ignore_callref($callref) and $time ne $record->{start};
        return $self->register_record($record);
    }

    if($method eq 'BYE')
    {
        $record->{stop} = $time
            unless exists $record->{stop} and $time < $record->{stop};
        return $self->register_record($record);
    }

    if($method eq 'CANCEL')
    {
        $record->{stop} = $time
            unless exists $record->{stop} and $time < $record->{stop};
        $self->mark_record_completed($record);
        return 0;
    }

    if($method eq 'ACK')
    {
        return 0;
    }
    
    if($method eq 'PRACK')
    {
        return 0;
    }
    
    $self->warn ("<p>Unknown method $method in $_<p><br>\n");
    return 0;
}

sub format_record($)
{
    my $self = shift;
    my $record = shift;
    
    # Print out using the Intermediate CDR format.
    return sprintf("%8s%8s%6d%6d%-15s%-15s%-15s%-6s%-6s\n",
        map { defined $record->{$_} ? $record->{$_} : '' }
            qw(date time duration billable_duration billable src dst src_type dst_type) );
}

1;
